#ifndef BOSS_Afterburner_Globals_Functions_Draw_H
#define BOSS_Afterburner_Globals_Functions_Draw_H

#include "Globals/Settings.h"
#include "RooAbsData.h"
#include "RooPlot.h"
#include "TCanvas.h"
#include "TH1.h"
#include "TH1D.h"
#include "TH2D.h"
#include "TLegend.h"
#include "TLine.h"
#include "TString.h"
#include "TTree.h"
#include "TVirtualPad.h"
#include <yaml-cpp/yaml.h>

namespace BOSS_Afterburner
{
  /// @addtogroup Afterburner_functions
  /// @{
  /// Functions for drawing and writing `TCanvas`, etc.
  /// @author  Remco de Boer 雷穆克 (r.e.deboer@students.uu.nl or remco.de.boer@ihep.ac.cn)
  /// @date    November 20th, 2018
  namespace Draw
  {
    template <class... ARGS>
    void DrawAndSaveRecursion(Option_t* opt, ARGS&&... args);
    ///< Start recursion for `DrawAndSaveRecursion`.
    template <class TYPE, class... ARGS>
    void DrawAndSaveRecursion(Option_t* opt, TYPE first, ARGS... args);
    template <>
    inline void DrawAndSaveRecursion(Option_t* opt){};
    ///< End recursion for `DrawAndSaveRecursion`.

    template <class... ARGS>
    void DrawAndSave(const char* saveas, Option_t* opt, const char* logScale, ARGS... args);
    void DrawAndSave(TH1& hist, const char* saveas, Option_t* opt, TString logScale = "");
    void DrawAndSave(TH1D& hist, const char* saveas, TString logScale = "");
    void DrawAndSave(TH2D& hist, const char* saveas, TString logScale = "");
    void DrawAndSave(TTree* tree, const char* varexp, const char* selection, Option_t* opt,
                     TString logScale = "");
    void DrawDifference(TH1* histToDraw, TH1* histToSubtract, Option_t* opt = "E1",
                        const char* setLog = "");
    void Indicate3SigmaRange(const YAML::Node& conf);
    void Indicate3SigmaRange(Double_t from, Double_t mid, Double_t to);
    void Indicate3SigmaRange(Double_t mass, Double_t width);
    void IndicateMassWindow(const YAML::Node& conf);
    void IndicateFitRange(const YAML::Node& conf);
    void PlotStatsOnFrame(RooAbsData& obj, RooPlot* frame);
    void SaveCanvas(const char* saveas, TVirtualPad* pad = gPad, TString logScale = "");
    void SetLogScale(TString logScale = "", TVirtualPad* pad = gPad);
    void SetLineCoordinates(TLine& line, Double_t x1, Double_t y1, Double_t x2, Double_t y2);

    void FormatLegend(TLegend* legend, const YAML::Node& conf);
    void SetCanvasTitle(const char* title);
  } // namespace Draw
  /// @}
} // namespace BOSS_Afterburner

/// The `DrawAndSaveRecursion` functions are necessary for `DrawAndSave`, which is a *variadic* template function.
template <class TYPE, class... ARGS>
inline void BOSS_Afterburner::Draw::DrawAndSaveRecursion(Option_t* opt, TYPE first, ARGS... args)
{
  /// Some useful information for defining template functions in the `.cxx` file:
  /// - https://isocpp.org/wiki/faq/templates#template-specialization-speed
  /// - https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl
  /// - https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl-export-keyword
  auto obj = dynamic_cast<TObject*>(first);
  if(obj) obj->Draw(opt);
  DrawAndSaveRecursion(opt, args...); // continue recursion
}

/// Function that allows you to draw and save any set of `TObject`s.
template <class... ARGS>
inline void BOSS_Afterburner::Draw::DrawAndSave(const char* saveas, Option_t* opt,
                                                const char* logScale, ARGS... args)
{
  TCanvas c;
  c.SetBatch();
  DrawAndSaveRecursion(opt, args...);
  SaveCanvas(saveas, &c, logScale);
}

#endif